/**
 * 
 */
package neptune.web.client.view.components.editor;

import java.util.ArrayList;
import java.util.Iterator;

import neptune.web.client.model.topology.SerializableLink;
import neptune.web.client.model.topology.SerializableNetworkInterface;
import neptune.web.client.model.topology.SerializableNode;
import neptune.web.client.model.topology.SerializableOLPIEndPoint;
import neptune.web.client.model.topology.SerializableProperty;
import neptune.web.client.model.topology.SerializableTopology;
import neptune.web.client.view.ConsoleManager;
import neptune.web.client.view.input.DefaultInputValidator;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * 
 */
public class LinkPropsDialog extends DialogBox {

	private TreeItem treeItem;
	private FlexTable mainPanel;
	private TextBox id;
	private CheckBox fullDuplex;
	private FlexTable sourceTable;
	private ListBox sourceNode;
	private SerializableTopology topology;
	private ListBox sourceInterface;
	private FlexTable destinationTable;
	private ListBox destinationNode;
	private ListBox destinationInterface;
	private ClickListener okListener = new ClickListener() {
		public void onClick(Widget sender) {
			if (validateInputs()) {
				// Aggiornamento grafo, elimina la connessione attuale per
				// ridisegnarla
				// alla fine del metodo, nel caso cambiassero gli endpoint
				Widget w1, w2 = null;
				if (link.getSource() != null && link.getDestination() != null) {
					w1 = null;
					for (int i = 0; i < editor.getGraphPanel().getWidgetCount(); i++) {
						if (editor.getGraphPanel().getWidget(i) instanceof Label) {
							Label label = (Label) editor.getGraphPanel()
									.getWidget(i);
							if (label.getText().equals(
									link.getSource().getNodeId()))
								w1 = label;
							else if (label.getText().equals(
									link.getDestination().getNodeId()))
								w2 = label;
						}
					}
					if (w1 != null && w2 != null) {
						editor.getGraphPanel().removeConnection(w1, w2);
						editor.updateGraphics();
					}
				}
				// Aggiornamento dati topologia
				link.setId(id.getText());
				link.setFullDuplex(fullDuplex.isChecked());
				// TODO supporto solo ad OLPI
				if (link.getSource() == null)
					link.setSource(new SerializableOLPIEndPoint());
				SerializableOLPIEndPoint source = (SerializableOLPIEndPoint) link
						.getSource();

				source.setNodeId(sourceNode.getItemText(sourceNode
						.getSelectedIndex()));
				source.setInterfaceId(sourceInterface
						.getItemText(sourceInterface.getSelectedIndex()));

				if (link.getDestination() == null)
					link.setDestination(new SerializableOLPIEndPoint());
				SerializableOLPIEndPoint destination = (SerializableOLPIEndPoint) link
						.getDestination();

				destination.setNodeId(destinationNode
						.getItemText(destinationNode.getSelectedIndex()));
				destination.setInterfaceId(destinationInterface
						.getItemText(destinationInterface.getSelectedIndex()));

				ArrayList<SerializableProperty> props = new ArrayList<SerializableProperty>(
						0);
				link.setProperties(props);
				treeItem.removeItems();
				if (!bandwidth.getText().equals("")) {
					SerializableProperty prop = new SerializableProperty();
					prop.setName(SerializableProperty.BANDWIDTH);
					prop.setValue(bandwidth.getText());
					props.add(prop);
					treeItem.addItem(prop.getName() + "=" + prop.getValue());
				}
				if (!delay.getText().equals("")) {
					SerializableProperty prop = new SerializableProperty();
					prop.setName(SerializableProperty.DELAY);
					prop.setValue(delay.getText());
					props.add(prop);
					treeItem.addItem(prop.getName() + "=" + prop.getValue());
				}
				if (!loss.getText().equals("")) {
					SerializableProperty prop = new SerializableProperty();
					prop.setName(SerializableProperty.LOSS);
					prop.setValue(loss.getText());
					props.add(prop);
					treeItem.addItem(prop.getName() + "=" + prop.getValue());
				}
				if (!duplication.getText().equals("")) {
					SerializableProperty prop = new SerializableProperty();
					prop.setName(SerializableProperty.DUPLICATE);
					prop.setValue(duplication.getText());
					props.add(prop);
					treeItem.addItem(prop.getName() + "=" + prop.getValue());
				}
				if (!corruption.getText().equals("")) {
					SerializableProperty prop = new SerializableProperty();
					prop.setName(SerializableProperty.CORRUPT);
					prop.setValue(corruption.getText());
					props.add(prop);
					treeItem.addItem(prop.getName() + "=" + prop.getValue());
				}

				// Aggiornamento albero
				LinkMenuBar widget = (LinkMenuBar) treeItem.getWidget();
				widget.setText(link.getId());

				// Aggiornamento grafo: ridisegna il link modificato
				w1 = null;
				w2 = null;
				for (int i = 0; i < editor.getGraphPanel().getWidgetCount(); i++) {
					if (editor.getGraphPanel().getWidget(i) instanceof Label) {
						Label label = (Label) editor.getGraphPanel().getWidget(
								i);
						if (label.getText()
								.equals(link.getSource().getNodeId()))
							w1 = label;
						else if (label.getText().equals(
								link.getDestination().getNodeId()))
							w2 = label;
					}
				}
				if (w1 != null && w2 != null) {
					editor.getGraphPanel().addConnection(w1, w2);
					editor.updateGraphics();
				}
				LinkPropsDialog.this.hide();
			}
		}
	};
	private ClickListener cancelListener = new ClickListener() {
		public void onClick(Widget sender) {
			if (cancelCallBack != null)
				cancelCallBack.onCancel();
			LinkPropsDialog.this.hide();
		}
	};
	private SerializableLink link;
	private ChangeListener sourceNodeListener = new ChangeListener() {
		public void onChange(Widget sender) {
			SerializableNode node = topology.getNode(sourceNode
					.getItemText(sourceNode.getSelectedIndex()));
			setInterfaceItems(node, true);
		}
	};
	private ChangeListener destinationNodeListener = new ChangeListener() {
		public void onChange(Widget sender) {
			SerializableNode node = topology.getNode(destinationNode
					.getItemText(destinationNode.getSelectedIndex()));
			setInterfaceItems(node, false);
		}
	};
	private TextBox bandwidth;
	private TextBox delay;
	private TextBox duplication;
	private TextBox loss;
	private TextBox corruption;
	private TopologyEditor editor;
	private CancelCallBack cancelCallBack;

	public LinkPropsDialog(SerializableLink link,
			SerializableTopology topology, TreeItem treeItem,
			TopologyEditor editor) {

		super(false, true);

		this.treeItem = treeItem;
		this.topology = topology;
		this.editor = editor;

		mainPanel = new FlexTable();
		mainPanel.setWidth("95%");
		ScrollPanel mainPanelWrapper = new ScrollPanel(mainPanel);
		mainPanelWrapper.setHeight("350px");

		/*
		 * Inizio costruzione campi per inserimemnto dati
		 */
		// ID
		Label idLabel = new Label("Link id");
		idLabel.addStyleName("NodePropsDialog-label");
		id = new TextBox();
		id.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(0, 0, idLabel);
		mainPanel.setWidget(0, 1, id);

		// FULL DUPLEX
		Label fullDuplexLabel = new Label("FullDuplex");
		fullDuplexLabel.addStyleName("NodePropsDialog-label");
		fullDuplex = new CheckBox();
		fullDuplex.addStyleName("NodePropsDialog-input");
		fullDuplex.setChecked(true);
		mainPanel.setWidget(1, 0, fullDuplexLabel);
		mainPanel.setWidget(1, 1, fullDuplex);

		// SOURCE
		DisclosurePanel sourcePanel = new DisclosurePanel("Source");
		sourcePanel.addStyleName("NodePropsDialog-inputPanel");
		sourcePanel.setAnimationEnabled(true);
		sourcePanel.setWidth("100%");
		mainPanel.setWidget(2, 0, sourcePanel);
		mainPanel.getFlexCellFormatter().setColSpan(2, 0, 2);

		sourceTable = new FlexTable();
		sourceTable.setWidth("100%");
		sourcePanel.add(sourceTable);

		// source node
		Label sourceNodeLabel = new Label("Node id");
		sourceNodeLabel.addStyleName("NodePropsDialog-label");
		sourceNode = new ListBox();
		sourceNode.addItem("");
		ArrayList<SerializableNode> nodes = topology.getNodes();
		for (Iterator<SerializableNode> iterator = nodes.iterator(); iterator
				.hasNext();) {
			SerializableNode node = iterator.next();
			sourceNode.addItem(node.getId());
		}
		sourceNode.addStyleName("NodePropsDialog-input");
		sourceNode.addChangeListener(sourceNodeListener);
		sourceTable.setWidget(0, 0, sourceNodeLabel);
		sourceTable.setWidget(0, 1, sourceNode);

		// source interface
		Label sourceInterfaceLabel = new Label("Interface name");
		sourceInterfaceLabel.addStyleName("NodePropsDialog-label");
		sourceInterface = new ListBox();
		sourceInterface.addStyleName("NodePropsDialog-input");
		sourceTable.setWidget(1, 0, sourceInterfaceLabel);
		sourceTable.setWidget(1, 1, sourceInterface);

		// DESTINATION
		DisclosurePanel destinationPanel = new DisclosurePanel("Destination");
		destinationPanel.addStyleName("NodePropsDialog-inputPanel");
		destinationPanel.setAnimationEnabled(true);
		destinationPanel.setWidth("100%");
		mainPanel.setWidget(3, 0, destinationPanel);
		mainPanel.getFlexCellFormatter().setColSpan(3, 0, 2);

		destinationTable = new FlexTable();
		destinationTable.setWidth("100%");
		destinationPanel.add(destinationTable);

		// destination node
		Label destinationNodeLabel = new Label("Node id");
		destinationNodeLabel.addStyleName("NodePropsDialog-label");
		destinationNode = new ListBox();
		destinationNode.addItem("");
		for (Iterator<SerializableNode> iterator = nodes.iterator(); iterator
				.hasNext();) {
			SerializableNode node = iterator.next();
			destinationNode.addItem(node.getId());
		}
		destinationNode.addStyleName("NodePropsDialog-input");
		destinationNode.addChangeListener(destinationNodeListener);
		destinationTable.setWidget(0, 0, destinationNodeLabel);
		destinationTable.setWidget(0, 1, destinationNode);

		// destination interface
		Label destinationInterfaceLabel = new Label("Interface name");
		destinationInterfaceLabel.addStyleName("NodePropsDialog-label");
		destinationInterface = new ListBox();
		destinationInterface.addStyleName("NodePropsDialog-input");
		destinationTable.setWidget(1, 0, destinationInterfaceLabel);
		destinationTable.setWidget(1, 1, destinationInterface);

		// BANDWIDTH
		Label bandwidthLabel = new Label("Bandwidth");
		bandwidthLabel.addStyleName("NodePropsDialog-label");
		bandwidth = new TextBox();
		bandwidth.setText("100kbps");
		bandwidth.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(4, 0, bandwidthLabel);
		mainPanel.setWidget(4, 1, bandwidth);

		// DELAY
		Label delayLabel = new Label("Delay");
		delayLabel.addStyleName("NodePropsDialog-label");
		delay = new TextBox();
		delay.setText("0ms");
		delay.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(5, 0, delayLabel);
		mainPanel.setWidget(5, 1, delay);

		// LOSS
		Label lossLabel = new Label("Loss rate");
		lossLabel.addStyleName("NodePropsDialog-label");
		loss = new TextBox();
		loss.setText("0.0%");
		loss.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(6, 0, lossLabel);
		mainPanel.setWidget(6, 1, loss);

		// DUPLICATION
		Label duplicationLabel = new Label("Duplication");
		duplicationLabel.addStyleName("NodePropsDialog-label");
		duplication = new TextBox();
		duplication.setText("0.0%");
		duplication.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(7, 0, duplicationLabel);
		mainPanel.setWidget(7, 1, duplication);

		// CORRUPTION
		Label corruptionLabel = new Label("Corruption");
		corruptionLabel.addStyleName("NodePropsDialog-label");
		corruption = new TextBox();
		corruption.setText("0.0%");
		corruption.addStyleName("NodePropsDialog-input");
		mainPanel.setWidget(8, 0, corruptionLabel);
		mainPanel.setWidget(8, 1, corruption);

		// HELP
		HTML help = new HTML("Link properties can be specified with "
				+ "more complete options using the "
				+ "<a href=\"http://www.linuxfoundation.org/en/Net:Netem\" "
				+ "target=\"_blank\">netem syntax</a>");
		mainPanel.setWidget(9, 0, help);
		mainPanel.getFlexCellFormatter().setColSpan(9, 0, 2);

		// BUTTON PANEL
		HorizontalPanel buttonPanel = new HorizontalPanel();
		Button ok = new Button("Ok");
		ok.addClickListener(okListener);
		ok.addStyleName("NodePropsDialog-button");
		Button cancel = new Button("Cancel");
		cancel.addClickListener(cancelListener);
		cancel.addStyleName("NodePropsDialog-button");
		buttonPanel.add(ok);
		buttonPanel.add(cancel);

		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setWidth("100%");
		vPanel.add(mainPanelWrapper);
		vPanel.add(buttonPanel);
		vPanel.setCellHorizontalAlignment(buttonPanel,
				VerticalPanel.ALIGN_RIGHT);

		this.setWidget(vPanel);
		this.setText("Link Properties");

		// Styles
		mainPanelWrapper.addStyleName("NodePropsDialog-mainWrapper");
		mainPanel.addStyleName("NodePropsDialog-mainPanel");
		this.addStyleName("NodePropsDialog");
		help.addStyleName("LinkPropsDialog-help");

		setData(link);
	}

	public boolean validateInputs() {
		// Resetta le segnalazioni d'errore
		id.removeStyleName("NodePropsDialog-input-error");
		sourceNode.removeStyleName("NodePropsDialog-input-error");
		destinationNode.removeStyleName("NodePropsDialog-input-error");
		sourceInterface.removeStyleName("NodePropsDialog-input-error");
		destinationInterface.removeStyleName("NodePropsDialog-input-error");

		DefaultInputValidator defaultValidator = new DefaultInputValidator();

		boolean[] errors = new boolean[5];
		for (int i = 0; i < errors.length; i++)
			errors[i] = true;

		if (!defaultValidator.isValid(id.getText())) {
			errors[0] = false;
			id.addStyleName("NodePropsDialog-input-error");
		}
		if (!defaultValidator.isValid(sourceNode.getItemText(sourceNode
				.getSelectedIndex()))) {
			errors[1] = false;
			sourceNode.addStyleName("NodePropsDialog-input-error");
		}
		if (!defaultValidator.isValid(destinationNode
				.getItemText(destinationNode.getSelectedIndex()))) {
			errors[2] = false;
			destinationNode.addStyleName("NodePropsDialog-input-error");
		}
		if (!defaultValidator.isValid(sourceInterface
				.getItemText(sourceInterface.getSelectedIndex()))) {
			errors[3] = false;
			sourceInterface.addStyleName("NodePropsDialog-input-error");
		}
		if (!defaultValidator.isValid(destinationInterface
				.getItemText(destinationInterface.getSelectedIndex()))) {
			errors[4] = false;
			destinationInterface.addStyleName("NodePropsDialog-input-error");
		}

		for (int i = 0; i < errors.length; i++) {
			if (!errors[i])
				return false;
		}

		return true;
	}

	public void setData(SerializableLink link) {
		id.setText(link.getId());
		fullDuplex.setChecked(link.isFullDuplex());
		if (link.getSource() != null) {
			int itemNumb = sourceNode.getItemCount();
			for (int i = 0; i < itemNumb; i++) {
				if (sourceNode.getItemText(i).equals(
						link.getSource().getNodeId())) {
					sourceNode.setSelectedIndex(i);
					break;
				}
			}
			SerializableNode node = topology.getNode(sourceNode
					.getItemText(sourceNode.getSelectedIndex()));
			setInterfaceItems(node, true);
			sourceInterface.addItem(link.getSource().getInterfaceId());
			itemNumb = sourceInterface.getItemCount();
			for (int i = 0; i < itemNumb; i++) {
				if (sourceInterface.getItemText(i).equals(
						link.getSource().getInterfaceId())) {
					sourceInterface.setSelectedIndex(i);
					break;
				}
			}
		}

		if (link.getDestination() != null) {
			int itemNumb = destinationNode.getItemCount();
			for (int i = 0; i < itemNumb; i++) {
				if (destinationNode.getItemText(i).equals(
						link.getDestination().getNodeId())) {
					destinationNode.setSelectedIndex(i);
					break;
				}
			}
			SerializableNode node = topology.getNode(destinationNode
					.getItemText(destinationNode.getSelectedIndex()));
			setInterfaceItems(node, false);
			destinationInterface
					.addItem(link.getDestination().getInterfaceId());
			itemNumb = destinationInterface.getItemCount();
			for (int i = 0; i < itemNumb; i++) {
				if (destinationInterface.getItemText(i).equals(
						link.getDestination().getInterfaceId())) {
					destinationInterface.setSelectedIndex(i);
					break;
				}
			}
		}

		ArrayList<SerializableProperty> properties = link.getProperties();
		for (Iterator<SerializableProperty> iterator = properties.iterator(); iterator
				.hasNext();) {
			SerializableProperty serializableProperty = iterator.next();

			if (serializableProperty.getName().equals(
					SerializableProperty.BANDWIDTH)) {
				bandwidth.setText(serializableProperty.getValue());
			}
			if (serializableProperty.getName().equals(
					SerializableProperty.DELAY)) {
				delay.setText(serializableProperty.getValue());
			}
			if (serializableProperty.getName()
					.equals(SerializableProperty.LOSS)) {
				loss.setText(serializableProperty.getValue());
			}
			if (serializableProperty.getName().equals(
					SerializableProperty.DUPLICATE)) {
				duplication.setText(serializableProperty.getValue());
			}
			if (serializableProperty.getName().equals(
					SerializableProperty.CORRUPT)) {
				corruption.setText(serializableProperty.getValue());
			}
		}

		this.link = link;
	}

	private void setInterfaceItems(SerializableNode node, boolean isSource) {
		if (isSource) {
			sourceInterface.clear();
			sourceInterface.addItem("");
		} else {
			destinationInterface.clear();
			destinationInterface.addItem("");
		}

		if (node == null) {
			ConsoleManager.addConsoleMessage("Link definition error - Link: "
					+ this.id.getText()
					+ " Cause: Undefined source or destination node", true);
			return;
		}

		// Costruisce un arraylist con tutte le interfacce gia' in uso per il
		// nodo sorgente
		ArrayList<String> usedIfs = new ArrayList<String>(0);
		usedIfs.add("eth0"); // TODO aggiunta manuale dell'interfaccia di
		// controllo fra quelle non selezionabili
		for (SerializableLink link : topology.getLinks()) {
			if (link.getSource() != null
					&& link.getSource().getNodeId() != null
					&& link.getSource().getNodeId().equals(node.getId())) {
				if (node.getInterfaceByName(link.getSource().getInterfaceId()) != null)
					usedIfs.add(link.getSource().getInterfaceId());
			}
			if (link.getDestination() != null
					&& link.getDestination().getNodeId() != null
					&& link.getDestination().getNodeId().equals(node.getId())) {
				if (node.getInterfaceByName(link.getDestination()
						.getInterfaceId()) != null)
					usedIfs.add(link.getDestination().getInterfaceId());
			}
		}

		for (SerializableNetworkInterface netIf : node.getInterfaces()) {
			if (isSource) {
				if (!usedIfs.contains(netIf.getInterfaceName()))
					sourceInterface.addItem(netIf.getInterfaceName());
			} else {
				if (!usedIfs.contains(netIf.getInterfaceName()))
					destinationInterface.addItem(netIf.getInterfaceName());
			}
		}
	}

	public void setCancelCallback(CancelCallBack cancelCallBack) {
		this.cancelCallBack = cancelCallBack;
	}

	/**
	 * @return the cancelCallBack
	 */
	public CancelCallBack getCancelCallBack() {
		return cancelCallBack;
	}
}
